#!/usr/bin/python2
import time
from enum import Enum
import os
import RPi.GPIO as GPIO
import signal

PID_FILE_PATH = os.path.realpath(os.path.join(os.path.dirname(__file__),'../fanCtl.pid'))

class Audion(Enum):
    PNP = 1
    NPN = 2

class FanController:
    """
    This class is designed to manage a cooling fan based on the temperature of a system,
    primarily intended for use with Raspberry Pi or similar single-board computers utilizing GPIO pins for hardware control.
    It integrates functionality to read CPU temperatures, control a transistor (either PNP or NPN type) that regulates power to the fan,
    and maintains a desired temperature range by automatically turning the fan on or off.
    """
    @staticmethod
    def getCpuTemp():
        with open("/sys/class/thermal/thermal_zone0/temp", 'r') as f:
            return float(f.read()) / 1000

    def __init__(self,
     port=12,
     audion=Audion.PNP,
     checkIntervalSc = 4.0,
     openTemp = 50,
     closeTemp = 40):
        """
        Create a fan controller
        :param port: Output pin for control signal [using GPIO.BOARD numbering], connected to the base of the transistorGPIO.BOARD], 连接三极管的基极
        :param audion: Type of transistor being used
        :param checkIntervalSc: Interval for checking temperature
        :param openTemp:  above (exclusive) which the fan should turn on
        :param closeTemp: below (exclusive) which the fan should turn off
        """
        if openTemp <= closeTemp:
            raise RuntimeError()
            
        self.audion(audion)
        self.__port = port
        self.__checkIntervalSc = checkIntervalSc
        self.__openTemp = openTemp
        self.__closeTemp = closeTemp
        
        
        
    def audion(self, audion):
        if not isinstance(audion, Audion):
            raise RuntimeError()

        self.__audion = audion

        if audion == Audion.PNP:
            self.__onFlag = GPIO.LOW
            self.__offFlag = GPIO.HIGH
        elif audion == Audion.NPN:
            self.__onFlag = GPIO.HIGH
            self.__offFlag = GPIO.LOW
        else:
            raise RuntimeError()

    def __on(self):
        GPIO.output(self.__port, self.__onFlag)

    def __off(self):
        GPIO.output(self.__port, self.__offFlag)

    def test(self):
        self.__initGPIO()
        fanClosed = True
        while True:
            GPIO.output(self.__port, fanClosed)
            fanClosed = not fanClosed
            print("flip",fanClosed)
            time.sleep(2.0)

    
    def __initGPIO(self):
        GPIO.setmode(GPIO.BOARD)
        GPIO.setwarnings(True)

        # open air fan first
        GPIO.setup(self.__port, GPIO.OUT)


    def start(self):
        self.__initGPIO()

        fanClosed = True
        while True:
            temp = FanController.getCpuTemp()
            print(f"\rTime: {time.ctime()} |Temp: {temp} | Fan[{fanClosed and 'CLOSE' or 'OPEN'}]","\033[K",end="")
            if fanClosed:
                if temp > self.__openTemp:
                    print('opening',end="")
                    self.__on()
                    fanClosed = not fanClosed
            else:
                if temp < self.__closeTemp:
                    print('closing',end="")
                    self.__off()
                    fanClosed = not fanClosed
                    
            time.sleep(self.__checkIntervalSc)


def appClear():
    GPIO.cleanup()
    os.remove(PID_FILE_PATH)
    

def savePid():
    with open(PID_FILE_PATH, 'w') as pid_file:
        pid_file.write(str(os.getpid()))
        print(f"Pid File Written: {PID_FILE_PATH}")


def handle_sigterm(signum, frame):
    appClear()
    exit(0)

# when starting, you can see the pid file on the project root dir.
# you can kill the controller script by the pid file.
if __name__ == '__main__':
    try:
        # register process-exit-handler
        signal.signal(signal.SIGTERM, handle_sigterm)
        # see the annotation below 'main' script
        savePid()
        ctl = FanController(checkIntervalSc=2.5)
        # ctl.test()
        # before, you can run test() to test you audio/fan/gpio etc.
        ctl.start()
    except KeyboardInterrupt:
        pass
    finally:
        appClear()
        print("\nStop Fan Control")
